/*==============================================
 * Task3a: Using Nodes in GDEV
 *
 * Written by <YOUR NAME HERE>
 *
 *==============================================*/

#include "SceneEngine.h"
#include "Node.h"
#include "GameUtils.h"
#include "Fail.h"
#include "Shot.h"
#include <sstream>
#include <time.h>

/** Task 3a1:
Proper 3D movement which includes the rotation of the player
*/
class CTask3a1: public CScene
{
	CXMesh* mpPlayerMesh;
	CMeshNode mPlayer;
	CXMesh* mpGroundMesh;

	void Enter()
	{
		mpGroundMesh=new CXMesh(GetDevice(),"media/checker.x");
		mpPlayerMesh=new CXMesh(GetDevice(),"media/teapot.x");
		SetupDefaultD3DOptions(GetDevice());
		mPlayer.Init(mpPlayerMesh,D3DXVECTOR3(0,0,0));
		// overhead view
		CameraLookAt(GetDevice(),D3DXVECTOR3(0,10,-5),D3DXVECTOR3(0,1,0));
	}
	void Update(float dt)
	{
		CScene::Update(dt);	// MUST CALL BASE CLASS'S Update
		if (IsTopMost()==false)	return;	// Pause if I'm not the topmost screen

		// rotation code:
		const float TURN_SPEED=D2R(45);
		if (CGameWindow::KeyDown(VK_LEFT))	mPlayer.Yaw(-TURN_SPEED*dt);
		if (CGameWindow::KeyDown(VK_RIGHT))	mPlayer.Yaw(+TURN_SPEED*dt);
		if (CGameWindow::KeyDown(VK_UP))		mPlayer.Pitch(-TURN_SPEED*dt);
		if (CGameWindow::KeyDown(VK_DOWN))	mPlayer.Pitch(+TURN_SPEED*dt);


		// basic movement:
		const float SPEED=1.0f;
		D3DXVECTOR3 move=GetKeyboardVector(WSAD_KEYS);
		// this is the same as...
		//if (CGameWindow::KeyDown('W'))	move.z++;
		//if (CGameWindow::KeyDown('S'))	move.z--;
		//if (CGameWindow::KeyDown('A'))	move.x--;
		//if (CGameWindow::KeyDown('D'))	move.x++;
		
		// this code only works if you don't turn
		//mPlayer.SetPos(mPlayer.GetPos()+move*dt*SPEED);
		// this code will work including the turn
		//mPlayer.Move(move*dt*SPEED);
		// this code will work, but never change the Y value
		mPlayer.MoveGround(move*dt*SPEED);
	}
	void Draw(float dt)
	{
		GetDevice()->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, BLACK_COL, 1.0f, 0 );
		GetDevice()->BeginScene();

		mpGroundMesh->Draw(D3DXVECTOR3(0,0,0));

		mPlayer.Draw();	// draw the player

		DrawTransitionFade();	// fade, just add it before the EndScene
		GetDevice()->EndScene();
		// note: no need to call Present, that is done in the CSceneEngine
	}
	void Leave()
	{
		SAFE_DELETE(mpGroundMesh);
		SAFE_DELETE(mpPlayerMesh);		
	}
};


/** Task 3a2:
Adding collision checking code
*/
class CTask3a2: public CScene
{
	CXMesh* mpPlayerMesh;
	CMeshNode mPlayer;
	CXMesh* mpGroundMesh;
	CXMesh* mpEnemyMesh;
	std::vector<CMeshNode*> mEnemies;

	void Enter()
	{
		mpGroundMesh=new CXMesh(GetDevice(),"media/checker.x");
		mpPlayerMesh=new CXMesh(GetDevice(),"media/teapot.x");
		mpEnemyMesh=new CXMesh(GetDevice(),"media/crate.x");
		SetupDefaultD3DOptions(GetDevice());
		mPlayer.Init(mpPlayerMesh,D3DXVECTOR3(0,0,0));
		// overhead view
		CameraLookAt(GetDevice(),D3DXVECTOR3(0,10,-5),D3DXVECTOR3(0,1,0));
		// add some enemies:
		for(int i=0;i<10;i++)
		{
			CMeshNode* ptr=new CMeshNode();
			ptr->Init(mpEnemyMesh,D3DXVECTOR3(randf(-5,5),0,randf(-5,5)));
			mEnemies.push_back(ptr);
		}
	}
	void Update(float dt)
	{
		CScene::Update(dt);	// MUST CALL BASE CLASS'S Update
		if (IsTopMost()==false)	return;	// Pause if I'm not the topmost screen

		// rotation code:
		const float TURN_SPEED=D2R(45);
		if (CGameWindow::KeyDown(VK_LEFT))	mPlayer.Yaw(-TURN_SPEED*dt);
		if (CGameWindow::KeyDown(VK_RIGHT))	mPlayer.Yaw(+TURN_SPEED*dt);
		if (CGameWindow::KeyDown(VK_UP))		mPlayer.Pitch(-TURN_SPEED*dt);
		if (CGameWindow::KeyDown(VK_DOWN))	mPlayer.Pitch(+TURN_SPEED*dt);

		// basic movement:
		const float SPEED=1.0f;
		D3DXVECTOR3 move=GetKeyboardVector(WSAD_KEYS);
		mPlayer.Move(move*dt*SPEED);

		// animation for fun:
		for(unsigned i=0;i<mEnemies.size();i++)
			mEnemies[i]->Yaw(D2R(dt*60));
		// check for collisions:
		bool collide=false;
		for(unsigned i=0;i<mEnemies.size();i++)
			if (CollisionMeshNode(&mPlayer,mEnemies[i]))
				collide=true;

		// collision display
		if (collide)
			GetEngine()->SetTitle("Collision");
		else
			GetEngine()->SetTitle("Node Testing");
	}
	void Draw(float dt)
	{
		GetDevice()->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, BLACK_COL, 1.0f, 0 );
		GetDevice()->BeginScene();

		mpGroundMesh->Draw(D3DXVECTOR3(0,0,0));

		mPlayer.Draw();	// draw the player
		mPlayer.DrawBounds(GetDevice());	// and its bounds
		DrawMeshNodes(mEnemies);
		for(unsigned i=0;i<mEnemies.size();i++)
			mEnemies[i]->DrawBounds(GetDevice());

		DrawTransitionFade();	// fade, just add it before the EndScene
		GetDevice()->EndScene();
		// note: no need to call Present, that is done in the CSceneEngine
	}
	void Leave()
	{
		SAFE_DELETE(mpGroundMesh);
		SAFE_DELETE(mpPlayerMesh);		
		SAFE_DELETE(mpEnemyMesh);
		DeleteMeshNodes(mEnemies);
	}
};

/** Task 3a3:
Adding shooting code
*/
class CTask3a3: public CScene
{
	CXMesh* mpPlayerMesh;
	CMeshNode mPlayer;
	CXMesh* mpGroundMesh;
	CXMesh* mpEnemyMesh;
	std::vector<CMeshNode*> mEnemies;
	CXMesh* mpShotMesh;
	std::vector<CMeshNode*> mShots;

	void Enter()
	{
		mpGroundMesh=new CXMesh(GetDevice(),"media/checker.x");
		mpPlayerMesh=new CXMesh(GetDevice(),"media/teapot.x");
		mpEnemyMesh=new CXMesh(GetDevice(),"media/crate.x");
		mpShotMesh=new CXMesh(GetDevice(),"media/yellow_ball.x");
		SetupDefaultD3DOptions(GetDevice());
		mPlayer.Init(mpPlayerMesh,D3DXVECTOR3(0,0,0));
		// overhead view
		CameraLookAt(GetDevice(),D3DXVECTOR3(0,10,-5),D3DXVECTOR3(0,1,0));
		// add some enemies:
		srand((unsigned)time(NULL));
		for(int i=0;i<10;i++)
		{
			CMeshNode* ptr=new CMeshNode();
			ptr->Init(mpEnemyMesh,D3DXVECTOR3(randf(-5,5),0,randf(-5,5)));
			mEnemies.push_back(ptr);
		}
	}
	void Update(float dt)
	{
		CScene::Update(dt);	// MUST CALL BASE CLASS'S Update
		if (IsTopMost()==false)	return;	// Pause if I'm not the topmost screen

		// rotation code:
		const float TURN_SPEED=D2R(45);
		if (CGameWindow::KeyDown(VK_LEFT))	mPlayer.Yaw(-TURN_SPEED*dt);
		if (CGameWindow::KeyDown(VK_RIGHT))	mPlayer.Yaw(+TURN_SPEED*dt);
		if (CGameWindow::KeyDown(VK_UP))		mPlayer.Pitch(-TURN_SPEED*dt);
		if (CGameWindow::KeyDown(VK_DOWN))	mPlayer.Pitch(+TURN_SPEED*dt);

		// basic movement:
		const float SPEED=1.0f;
		D3DXVECTOR3 move=GetKeyboardVector(WSAD_KEYS);
		mPlayer.Move(move*dt*SPEED);

		// animation for fun:
		for(unsigned i=0;i<mEnemies.size();i++)
			mEnemies[i]->Yaw(D2R(dt*60));

		// players shots:
		if (CGameWindow::KeyPress(VK_SPACE))
		{
			// shoot:
			CShot* shot=new CShot();
			// this is the direction the player is facing
			D3DXVECTOR3 dir=mPlayer.RotateVector(D3DXVECTOR3(0,0,1));
			// setup the shot starting at player location & direction
			shot->Init(mpShotMesh,mPlayer.GetPos(),mPlayer.GetHpr());
			shot->mPos+=dir*0.25f;	// move it a bit forward
			shot->mVel=dir*5;	// velocity 
			shot->mScale=0.3f;	// small
			shot->mLife=1000;	// lives for 1 second
			mShots.push_back(shot);	// add to vector
		}

		// move shots:
		UpdateMeshNodes(mShots,dt);
		// collisions:
		for(int sh=0;sh<mShots.size();sh++)
		{
			for(int en=0;en<mEnemies.size();en++)
			{
                //TODO("Check for collisions");
                // if it occurs, destroy the shot and do 50 damage to the enemy
				if (CollisionMeshNode(mShots[sh], mEnemies[en]))
				{
					mShots[sh]->Destroy();
					mEnemies[en]->Damage(50);
				}
			}
		}

        //TODO remove corpses:
        // otherwise you will have lots of dead enemies around
		DeleteDeadMeshNodes(mShots);
		DeleteDeadMeshNodes(mEnemies);

		// display
		std::stringstream sout;
		sout<<"#shots "<<mShots.size()<<" #enemies "<<mEnemies.size();
		GetEngine()->SetTitle(sout.str().c_str());

	}
	void Draw(float dt)
	{
		GetDevice()->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, BLACK_COL, 1.0f, 0 );
		GetDevice()->BeginScene();

		mpGroundMesh->Draw(D3DXVECTOR3(0,0,0));

		mPlayer.Draw();	// draw the player
		mPlayer.DrawBounds(GetDevice());	// and its bounds
		DrawMeshNodes(mEnemies);
		for(unsigned i=0;i<mEnemies.size();i++)
			mEnemies[i]->DrawBounds(GetDevice());
		DrawMeshNodes(mShots);

		DrawTransitionFade();	// fade, just add it before the EndScene
		GetDevice()->EndScene();
		// note: no need to call Present, that is done in the CSceneEngine
	}
	void Leave()
	{
		SAFE_DELETE(mpGroundMesh);
		SAFE_DELETE(mpPlayerMesh);		
		SAFE_DELETE(mpEnemyMesh);
		SAFE_DELETE(mpShotMesh);
		DeleteMeshNodes(mEnemies);
		DeleteMeshNodes(mShots);
	}
};

int WINAPI WinMain(HINSTANCE hInstance, 
				   HINSTANCE hPrevInstance, 
				   LPTSTR lpCmdLine, int nCmdShow)
{
    ENABLE_LEAK_DETECTION();	// Enable run-time memory check for debug builds.
	CSceneEngine eng(640,480,false);
	eng.AddScene(new CTask3a3());
	return eng.Go(hInstance);	// runs everything
}
